/**
 * Class Global
 * 
 * @author Hussein Patwa
 * @date 20 April 2008 - Rev 09 May 2008
 * @version 5.0 Implementation of the global trending algorithm
 */
package trend;

import java.util.*;
import java.lang.Double;

@SuppressWarnings("all")
public class Global {

  /**
   * Constructor for global
   * 
   * @param paths Container for paths
   * @param path Container for path under observation
   * @param DATA Container for the original data to be analysed
   * @param minLength Minimum length after which trend is considered
   *          valid
   * @param maxLength Maximum length after which trend is considered
   *          final
   * @param min_conf Minimum confidence value
   */

  ArrayList path; // ArrayList to hold spatial objects
  ArrayList DATA;
  Stack<ArrayList> paths;
  HashSet<ArrayList> lastpaths;
  double lastcorrelation;

  int minlength; // length above which path is considered established
  int maxlength; // length above which path is assumed
  double min_conf; // min_conf as noted in pseudo code

  class observation {
    /**
     * Constructor for an observation
     * 
     * @param dist Distance from X co-ordinate
     * @param diff Distance from Y co-ordinate
     */
    double dist;
    double diff;

    /**
     * Observation
     * 
     * @param dist Distance from X co-ordinate
     * @param diff Distance from Y co-ordinate
     */
    observation(double dist, double diff) {
      this.dist = dist;
      this.diff = diff;
    }
  }

  /**
   * Initialise parameters for global trending
   * 
   * @param neighbourInfo Neighbourhood cluster information
   * @param data Originating data
   * @param index Index number of the current map position
   * @param minLength Minimum length after which trend is considered
   *          valid
   * @param maxLength Maximum length after which trend is considered
   *          final
   * @param min_conf Minimum confidence value
   */
  public Global(NeighbourhoodInfo neighbourInfo, ArrayList data,
      int index, int minLength, int maxLength, double min_conf) {
    this.minlength = minLength;
    this.maxlength = maxLength;
    this.min_conf = min_conf;
    paths = computeAllPaths(neighbourInfo, index, minLength);
    System.out.println("Number of Paths: " + paths.size());
    // for(ArrayList path:paths){
    // System.out.println(path.size());
    // }
    findTrends(index, data, neighbourInfo);
  }

  Stack computeAllPaths(NeighbourhoodInfo neighbourInfo, int index,
      int minLength) {
    /**
     * Generate a list of paths from the neighbourhood info
     * 
     * @param neighbourInfo Neighbourhood spatial info from abdnMat.csv
     * @param index Index number for each spatial area
     * @param minLength Minimum length after which trend is considered
     *          valid
     * @return HashSet
     */

    Stack rtn = new Stack();
    Vector tempNeighbours = neighbourInfo
        .getNeighbourhoodAreaIDs(index);
    // System.out.println("index: " + index + " neighbours: "
    // + tempNeighbours);
    for (Iterator i = tempNeighbours.iterator(); i.hasNext();) {
      Integer node = (Integer) i.next();
      ArrayList tempPath = new ArrayList();
      tempPath.add(new Integer(index));
      tempPath.add(new Integer(node));
      rtn.add(tempPath);
      // System.out.println(tempPath);
    }
    System.out.println(rtn.size());
    for (int j = 0; j < minLength - 1; j++)
      rtn.addAll(extendAllPaths(rtn, neighbourInfo));
    return rtn;
  }

  Stack extendAllPaths(Stack paths, NeighbourhoodInfo neighbourInfo) {
    Stack rtn = new Stack();
    while (!paths.isEmpty()) {
      ArrayList path = (ArrayList) paths.pop();
      int index = ((Integer) path.get(path.size() - 1)).intValue();
      Vector tempNeighbours = neighbourInfo
          .getNeighbourhoodAreaIDs(index);
      // System.out.println("index: " + index + " neighbours: "
      // + tempNeighbours);
      for (Iterator i = tempNeighbours.iterator(); i.hasNext();) {
        Integer node = (Integer) i.next();
        if (!path.contains(node)) {
          ArrayList tempPath = new ArrayList();
          tempPath.addAll(path);
          tempPath.add(node);
          rtn.add(tempPath);
          // System.out.println(tempPath);
        }
      }
    }
    return rtn;
  }

  public void findTrends(int index, ArrayList data,
      NeighbourhoodInfo neighbourInfo) {
    /**
     * Find all available trends
     * 
     * @param index Index number for each spatial area
     * @param data Original data set
     * @return null
     */
    lastpaths = new HashSet<ArrayList>();
    lastcorrelation = 0;
    int pathlength = minlength;
    int first_pos = 0;
    int last_pos = minlength;
    TreeMap PathObs = new TreeMap();
    while (!paths.isEmpty()) {
      ArrayList path = (ArrayList) paths.pop(); // Move 1st element of
      // paths to path
      for (int j = first_pos; j < last_pos; j++) {
        // System.out.println(path.size());
        Integer currentIndex = (Integer) path.get(j);
        Spatialobject o = (Spatialobject) data.get(index);
        Spatialobject object = (Spatialobject) data.get(currentIndex);
        double dist = Math.sqrt(Math.pow((object.getX() - o.getX()), 2)
            + Math.pow((object.getY() - o.getY()), 2)); // calculate
        // difference
        // in x
        // co-ordinate

        double diff = object.getV() - o.getV(); // calculate difference
        // in y
        // co-ordinate

        // observation obsTemp = new observation(dist,diff); // insert
        // distX into observations
        PathObs.put(diff, dist);// insert distY into observations
      }
    }
    Regression reg = new Regression(PathObs);
    // TreeMap predObs = reg.getRegressionPredictions();
    // System.out.println(PathObs + " " + predObs);

    // Correlation
    Correlation corr = new Correlation(PathObs);
    double cor = corr.getCorrelationCoefficient();

    if (Math.abs(cor) > min_conf) {
      // if the length of the paths with trend is less than maxlength
      // then try to check if the trend exists when the length is
      // extended by one more area
      lastcorrelation = cor;
      lastpaths.addAll(paths);
      if (pathlength < maxlength + 2) {
        paths = extendAllPaths(paths, neighbourInfo);
        pathlength++;
        first_pos = last_pos;
        last_pos++;
      } else
        paths.clear();
    }
  }

  public void viewTrends() {
    /**
     * Print all of the available trends
     * 
     * @return null
     */
    for (ArrayList<Integer> p : lastpaths) {
      // print area names from each path
      for (Integer index : p) {
        Spatialobject o = (Spatialobject) DATA.get(index);
        System.out.print(o.getName() + " ");
      }
      System.out.println();
    }

    System.out.println();
  }

}
